﻿using HtmlAgilityPack;
using Playnite.SDK;
using Playnite.SDK.Data;
using Playnite.SDK.Events;
using Playnite.SDK.Models;
using Playnite.SDK.Plugins;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interop;
using System.Xml.Linq;

namespace HtmlDispatcher
{
    public class HtmlDispatcher : GenericPlugin
    {
        private string htmlLibraryPath = @"E:\PlayniteLibraryHtml";
        private bool cleanup = false;
        private Dictionary<string, LocalGameMetadata> localGameMetadataDict = new Dictionary<string, LocalGameMetadata>();
        public Dictionary<string, LocalGameMetadata> LocalGameMetadataDict { get { return this.localGameMetadataDict; } }
        private Dictionary<string, string> externalLinkMappingDict = new Dictionary<string, string>();

        private static readonly ILogger logger = LogManager.GetLogger();

        private HtmlDispatcherSettingsViewModel settings { get; set; }

        public override Guid Id { get; } = Guid.Parse("23b67833-380d-4fa6-84c4-143d6fc8ff94");

        public HtmlDispatcher(IPlayniteAPI api) : base(api)
        {
            settings = new HtmlDispatcherSettingsViewModel(this);
            Properties = new GenericPluginProperties
            {
                HasSettings = true
            };
            this.InitializeLocalGameMetadatas();
        }

        public void InitializeLocalGameMetadatas()
        {
            try
            {
                var localGameMetadataCollection = Serialization.FromJsonFile<LocalGameMetadataCollection>(@"D:\projects\kp-playnite\PlayniteExtensions-JimmyMo\LocalGameMapping.json");
                foreach (var localGameMetadata in localGameMetadataCollection)
                {
                    this.localGameMetadataDict[localGameMetadata.PID] = localGameMetadata;
                }
            }
            catch (Exception ex)
            {
                PlayniteApi.Notifications.Add(new NotificationMessage(
                    "ReadLocalGameMappings",
                    ex.Message,
                    NotificationType.Error,
                    () => OpenSettingsView()));
            }
        }

        public static void CopyDirectory(string sourceDir, string targetDir)
        {
            // 创建目标目录
            Directory.CreateDirectory(targetDir);

            // 获取源目录中的文件
            string[] files = Directory.GetFiles(sourceDir);

            foreach (string file in files)
            {
                // 构建目标文件路径
                string destFile = Path.Combine(targetDir, Path.GetFileName(file));
                // 复制文件
                File.Copy(file, destFile, true); // true 表示如果目标文件已存在，则覆盖
            }

            // 获取源目录中的子目录
            string[] dirs = Directory.GetDirectories(sourceDir);

            foreach (string dir in dirs)
            {
                // 构建目标子目录路径
                string destDir = Path.Combine(targetDir, Path.GetFileName(dir));
                // 递归复制子目录
                CopyDirectory(dir, destDir);
            }
        }

        public void ClearExternalLinks(List<Game> gameslist)
        {
            LoadExternalLinkMappingDict();

            foreach (Game game in gameslist)
            {
                //Console.WriteLine(game.Description);
                HtmlDocument doc = new HtmlDocument();
                doc.LoadHtml(game.Description);
                var nodes = doc.DocumentNode.SelectNodes("//img");
                if (nodes != null)
                {
                    foreach (var node in nodes)
                    {
                        string imgSrc = node.GetAttributeValue("src", string.Empty);
                        string imgDataSrc = node.GetAttributeValue("data-src", string.Empty);
                        string src = string.Empty;
                        if (!string.IsNullOrEmpty(imgSrc))
                        {
                            src = imgSrc;
                        }
                        else
                        {
                            src = imgDataSrc;
                        }

                        string externalImgPattern = @"http[s]?://[^""']+\.(jpg|jpeg|png|gif)";
                        if (Regex.IsMatch(src, externalImgPattern))
                        {
                            string targetImgName = PathUtils.ExtractFilenameFromUrl(src);
                            string targetImgBaseSrc = $@"http://127.0.0.1:4090";
                            string targetImgPath = $@"{targetImgBaseSrc}/{game.Id.ToString()}/{targetImgName}";
                            //Path.Combine(targetImgBaseSrc, game.Id.ToString(), targetImgName);
                            // img['src'] = imgPath; // 在C#中，这将取决于你的img对象是如何定义的
                            string targetImgPathSha1 = CryptoUtils.CalculateSHA1(targetImgPath);
                            node.SetAttributeValue("src", targetImgPath);
                            if (!externalLinkMappingDict.ContainsKey(targetImgPathSha1))
                            {
                                File.AppendAllText("externalLinkMapping.txt", $"{targetImgPathSha1} -> {src} ### {targetImgPath}\n");
                                externalLinkMappingDict[targetImgPathSha1] = src;
                            }
                        }
                    }
                }
                game.Description = doc.DocumentNode.OuterHtml;
                PlayniteApi.Database.Games.Update(game);
                //PlayniteApi.Database.Games[game.Id].Description = doc.DocumentNode.OuterHtml;
            }
        }

        private void LoadExternalLinkMappingDict()
        {
            //Dictionary<string, string> dict = new Dictionary<string, string>();
            externalLinkMappingDict.Clear();
            string[] lines = File.ReadAllLines("externalLinkMapping.txt");
            foreach (string line in lines)
            {
                string[] parts = Regex.Split(line, @"->|###");
                externalLinkMappingDict[parts[0].Trim()] = parts[1].Trim();
            }
        }

        public void RollbackExternalLinks(List<Game> gameslist)
        {
            LoadExternalLinkMappingDict();

            foreach (Game game in gameslist)
            {
                HtmlDocument doc = new HtmlDocument();
                doc.LoadHtml(game.Description);
                var nodes = doc.DocumentNode.SelectNodes("//img");
                if (nodes != null)
                {
                    foreach (var node in nodes)
                    {
                        string imgSrc = node.GetAttributeValue("src", string.Empty);
                        string imgDataSrc = node.GetAttributeValue("data-src", string.Empty);
                        string src = string.Empty;
                        if (!string.IsNullOrEmpty(imgSrc))
                        {
                            src = imgSrc;
                        }
                        else
                        {
                            src = imgDataSrc;
                        }

                        string internalImgPattern = @"http[s]?://127.0.0.1:4090[^""']+\.(jpg|jpeg|png|gif)";
                        if (Regex.IsMatch(src, internalImgPattern))
                        {
                            string targetImgPathSha1 = CryptoUtils.CalculateSHA1(src);
                            if (externalLinkMappingDict.ContainsKey(targetImgPathSha1))
                            {
                                string originalImgPath = externalLinkMappingDict[targetImgPathSha1].Trim();
                                node.SetAttributeValue("src", originalImgPath);
                            }
                            else
                            {
                                PlayniteApi.Notifications.Add(new NotificationMessage("DispatchHtmlError", $"Rollback failed, origianal path not found. {game.Name}", NotificationType.Error));
                            }
                        }
                    }
                }
                game.Description = doc.DocumentNode.OuterHtml;
                PlayniteApi.Database.Games.Update(game);
            }
        }

        public void DoDispatchHtml(List<Game> gameslist)
        {
            var progressOptions = new GlobalProgressOptions(String.Empty, true)
            {
                IsIndeterminate = false
            };
            _ = PlayniteApi.Dialogs.ActivateGlobalProgress((progressAction) =>
            {
                progressAction.ProgressMaxValue = gameslist.Count;
                int index = 0;
                foreach (Game game in gameslist)
                {
                    // 获取每个game下的Id这个Guid，然后去HtmlLibrary目录下寻找<guid>.html和<guid>目录
                    // 然后解析game下的Notes，用正则表达式找到PID
                    // 然后加载LocalGameMapping.json，通过PID找到RepoPath，即这个游戏安装文件目录
                    // 然后将<guid>.html复制到目录
                    // 将<guid>目录下的图片文件重命名后复制到目录
                    index++;
                    progressAction.Text = $"{index}/{progressAction.ProgressMaxValue} {game.Name}";
                    progressAction.CurrentProgressValue = index;
                    if (progressAction.CancelToken.IsCancellationRequested)
                    {
                        break;
                    }

                    string pid = game.GetPid();
                    if (!this.LocalGameMetadataDict.ContainsKey(pid))
                    {
                        PlayniteApi.Notifications.Add(new NotificationMessage("DispatchHtmlError", $"Game metadata not found {pid}", NotificationType.Error));
                        continue;
                    }
                    string repoPath = this.localGameMetadataDict[pid].RepoPath;
                    string guid = game.Id.ToString();
                
                    try
                    {
                        if (cleanup)
                        {
                            if (File.Exists($@"{repoPath}\{guid}.html"))
                            {
                                File.Delete($@"{repoPath}\{guid}.html");
                            }
                            if (Directory.Exists($@"{repoPath}\{guid}"))
                            {
                                Directory.Delete($@"{repoPath}\{guid}", true);
                            }
                            if (Directory.Exists($@"{repoPath}\downloaded_images"))
                            {
                                Directory.Delete($@"{repoPath}\downloaded_images", true);
                            }
                        }
                        else
                        {
                            File.Copy($@"{htmlLibraryPath}\{guid}.html", $@"{repoPath}\{guid}.html", true);
                            if (Directory.Exists($@"{htmlLibraryPath}\{guid}"))
                            {
                                CopyDirectory($@"{htmlLibraryPath}\{guid}", $@"{repoPath}\{guid}");
                            }
                            if (Directory.Exists($@"{htmlLibraryPath}\downloaded_images\{guid}"))
                            {
                                CopyDirectory($@"{htmlLibraryPath}\downloaded_images\{guid}", $@"{repoPath}\downloaded_images\{guid}");
                            }
                        }
                        try
                        {
                            // 将图片导出为icon, banner和cover
                            DirectoryInfo dir = new DirectoryInfo($@"{htmlLibraryPath}\{guid}\");
                            foreach (FileInfo file in dir.EnumerateFiles())
                            {
                                string destImageFileName = "";
                                ImageType imageType = ImageUtils.GetImageType(file.FullName);
                                if (imageType == ImageType.Icon)
                                {
                                    destImageFileName = "icon";
                                }
                                else if (imageType == ImageType.Banner)
                                {
                                    destImageFileName = "banner";
                                }
                                else if (imageType == ImageType.Cover)
                                {
                                    destImageFileName = "cover";
                                }
                                else
                                {
                                    destImageFileName = Path.GetFileNameWithoutExtension(file.Name);
                                    PlayniteApi.Notifications.Add(new NotificationMessage("DispatchHtmlImageTypeWarn", $"Unknown Image: {file.FullName}", NotificationType.Info));
                                }
                                
                                if (cleanup)
                                {
                                    //if (File.Exists($@"{repoPath}\{destImageFileName}.{file.Extension}"))
                                    //{
                                    //    File.Move($@"{repoPath}\{destImageFileName}.{file.Extension}", $@"{repoPath}\{destImageFileName}{file.Extension}");
                                    //}
                                    if (File.Exists($@"{repoPath}\{destImageFileName}{file.Extension}"))
                                    {
                                        File.Delete($@"{repoPath}\{destImageFileName}{file.Extension}");
                                    }
                                }
                                else
                                {
                                    File.Copy(file.FullName, $@"{repoPath}\{destImageFileName}{file.Extension}", true);
                                }
                            }
                        } catch (Exception ex)
                        {
                            PlayniteApi.Notifications.Add(new NotificationMessage("DispatchHtmlError", ex.Message, NotificationType.Error));
                        }
                    } catch (IOException ex)
                    {
                        PlayniteApi.Notifications.Add(new NotificationMessage("DispatchHtmlError", ex.Message, NotificationType.Error));
                        continue;
                    }
                }
                PlayniteApi.Dialogs.ShowMessage("已完成Html和图片文件的派遣!", "Html Dispatcher");
            }, progressOptions);
        }

        public override void OnGameInstalled(OnGameInstalledEventArgs args)
        {
            // Add code to be executed when game is finished installing.
        }

        public override void OnGameStarted(OnGameStartedEventArgs args)
        {
            // Add code to be executed when game is started running.
        }

        public override void OnGameStarting(OnGameStartingEventArgs args)
        {
            // Add code to be executed when game is preparing to be started.
        }

        public override void OnGameStopped(OnGameStoppedEventArgs args)
        {
            // Add code to be executed when game is preparing to be started.
        }

        public override void OnGameUninstalled(OnGameUninstalledEventArgs args)
        {
            // Add code to be executed when game is uninstalled.
        }

        public override void OnApplicationStarted(OnApplicationStartedEventArgs args)
        {
            // Add code to be executed when Playnite is initialized.
        }

        public override void OnApplicationStopped(OnApplicationStoppedEventArgs args)
        {
            // Add code to be executed when Playnite is shutting down.
        }

        public override void OnLibraryUpdated(OnLibraryUpdatedEventArgs args)
        {
            // Add code to be executed when library is updated.
        }

        public override ISettings GetSettings(bool firstRunSettings)
        {
            return settings;
        }

        public override UserControl GetSettingsView(bool firstRunSettings)
        {
            return new HtmlDispatcherSettingsView();
        }

        public override IEnumerable<MainMenuItem> GetMainMenuItems(GetMainMenuItemsArgs args)
        {
            List<MainMenuItem> mainMenuItems = new List<MainMenuItem>
            {
                new MainMenuItem
                {
                    MenuSection = "@Html 分派器",
                    //Icon = Path.Combine(pluginFolder, "icon.png"),
                    Description = "分派（已筛选游戏）",
                    Action = (MainMenuItem) =>
                    {
                        cleanup = false;
                        DoDispatchHtml(PlayniteApi.MainView.FilteredGames.ToList());
                    }
                },
                new MainMenuItem
                {
                    MenuSection = "@Html 分派器",
                    //Icon = Path.Combine(pluginFolder, "icon.png"),
                    Description = "分派（所有游戏）",
                    Action = (MainMenuItem) =>
                    {
                        cleanup = false;
                        DoDispatchHtml(PlayniteApi.Database.Games.ToList());
                        //List<string> ErrorList = new List<string>();
                        //if (Settings.VerifySettings(out ErrorList))
                        //{
                        //    DoExportToHtml(PlayniteApi.Database.Games.ToList());
                        //}
                        //else
                        //{
                        //    StringBuilder errorbuilder = new StringBuilder();
                        //    foreach (string error in ErrorList)
                        //    {
                        //        errorbuilder.Append(error + "\n");
                        //    }
                        //    PlayniteApi.Dialogs.ShowMessage(errorbuilder.ToString(), Constants.AppName);
                        //}
                    }
                },
                new MainMenuItem
                {
                    MenuSection = "@Html 分派器",
                    //Icon = Path.Combine(pluginFolder, "icon.png"),
                    Description = "清除（已筛选游戏）",
                    Action = (MainMenuItem) =>
                    {
                        cleanup = true;
                        DoDispatchHtml(PlayniteApi.MainView.FilteredGames.ToList());
                    }
                },
                new MainMenuItem
                {
                    MenuSection = "@Html 分派器",
                    //Icon = Path.Combine(pluginFolder, "icon.png"),
                    Description = "清除（所有游戏）",
                    Action = (MainMenuItem) =>
                    {
                        cleanup = true;
                        DoDispatchHtml(PlayniteApi.Database.Games.ToList());
                    }
                },
                new MainMenuItem
                {
                    MenuSection = "@Html 分派器",
                    //Icon = Path.Combine(pluginFolder, "icon.png"),
                    Description = "刷新外链（已筛选游戏）",
                    Action = (MainMenuItem) =>
                    {
                        ClearExternalLinks(PlayniteApi.MainView.FilteredGames.ToList());
                    }
                },
                //new MainMenuItem
                //{
                //    MenuSection = "@Html 分派器",
                //    //Icon = Path.Combine(pluginFolder, "icon.png"),
                //    Description = "刷新外链（所有游戏）",
                //    Action = (MainMenuItem) =>
                //    {
                //        ClearExternalLinks(PlayniteApi.Database.Games.ToList());
                //    }
                //},
                new MainMenuItem
                {
                    MenuSection = "@Html 分派器",
                    //Icon = Path.Combine(pluginFolder, "icon.png"),
                    Description = "回滚外链（已筛选游戏）",
                    Action = (MainMenuItem) =>
                    {
                        RollbackExternalLinks(PlayniteApi.MainView.FilteredGames.ToList());
                    }
                },
                //new MainMenuItem
                //{
                //    MenuSection = "@Html 分派器",
                //    //Icon = Path.Combine(pluginFolder, "icon.png"),
                //    Description = "回滚外链（所有游戏）",
                //    Action = (MainMenuItem) =>
                //    {
                //        ClearExternalLinks(PlayniteApi.Database.Games.ToList());
                //    }
                //},
            };
            return mainMenuItems;
        }
    }
}